<Serializable()> _
Public Class InCriteria
    Implements ICriteriaComponent

    Protected mSubQuery As SelectQuery
    Protected mItems As Generic.List(Of String)
    Protected mLeftExpression As String
    Protected mRightExpression As String
    Protected mNegate As Boolean = False

    Public Sub New()
        mItems = New List(Of String)
    End Sub

    Public Sub New(ByVal LeftExpression As String, ByVal Items As IEnumerable(Of Object))
        mLeftExpression = LeftExpression
        mItems = New List(Of String)
        For Each Item As Object In Items
            mItems.Add(Item.ToString)
        Next
    End Sub

    Public Sub New(ByVal LeftExpression As String, ByVal Items As IEnumerable(Of Integer))
        mLeftExpression = LeftExpression
        mItems = New List(Of String)
        For Each Item As Integer In Items
            mItems.Add(Item.ToString)
        Next
    End Sub

    Public Sub New(ByVal LeftExpression As String, ByVal Items As IEnumerable(Of String))
        mLeftExpression = LeftExpression
        mItems = New List(Of String)(Items)
    End Sub

    Public Sub New(ByVal LeftExpression As String, ByVal SubQuery As SelectQuery)
        mLeftExpression = LeftExpression
        mSubQuery = SubQuery
    End Sub

    Public Sub New(ByVal LeftExpression As String, ByVal InField As String)
        mLeftExpression = LeftExpression
        mRightExpression = InField
    End Sub

    Public Property LeftExpression() As String
        Get
            Return mLeftExpression
        End Get
        Set(ByVal value As String)
            mLeftExpression = value
        End Set
    End Property

    Public Property RightExpression() As String
        Get
            Return mRightExpression
        End Get
        Set(ByVal value As String)
            mRightExpression = value
        End Set
    End Property

    Public Property LeftExpressionType As ExpressionType = ExpressionType.Auto
    Public Property RightExpressionType As ExpressionType = ExpressionType.Auto

    Public Property SubQuery() As SelectQuery
        Get
            Return mSubQuery
        End Get
        Set(ByVal value As SelectQuery)
            mSubQuery = value
            mItems = Nothing
        End Set
    End Property

    Public ReadOnly Property Items() As Generic.List(Of String)
        Get
            Return mItems
        End Get
    End Property

    Public Property Negate() As Boolean
        Get
            Return mNegate
        End Get
        Set(ByVal value As Boolean)
            mNegate = value
        End Set
    End Property

    Public Sub AddItem(ByVal Item As String)
        mSubQuery = Nothing
        mItems.Add(Item)
    End Sub

    Public Function GetText() As String Implements ICriteriaComponent.GetText
        Return GetText(QueryOptions.DefaultOptions)
    End Function

    Public Function GetText(Options As QueryOptions) As String Implements ICriteriaComponent.GetText
        Dim Query As String = ""
        If mNegate Then Query = "NOT "
        Query &= PrepLeftExpr(mLeftExpression, Options) & " IN ("
        If mSubQuery IsNot Nothing Then
            If SubQuery.Fields.Count <> 1 Then
                Throw New InvalidOperationException("Subqueries within INCriteria must contain exactly 1 field.")
            End If
            Query &= mSubQuery.GetSQLText
        ElseIf mRightExpression <> "" Then
            Query &= PrepRightExpr(mRightExpression, Options)
        Else
            If mItems.Count = 0 Then
                Throw New InvalidOperationException("The list of items in this INCriteria is empty.")
            End If
            Dim List As String = ""
            For Each item As String In mItems
                List &= "'" & Replace(item, "'", "''") & "',"
            Next
            If mItems.Count > 0 Then List = List.Substring(0, List.Length - 1)
            Query &= List
        End If
        Query &= ")"
        Return Query
    End Function

    Private Function PrepLeftExpr(Expression As String, options As QueryOptions) As String
        If LeftExpressionType = ExpressionType.Exact Then
            Return Expression
        End If

        Dim ret As String = ""

        Dim UseQuotes As Boolean = False
        Dim Escape As Boolean = False
        If LeftExpressionType = ExpressionType.Literal AndAlso Not IsNumeric(Expression) Then
            UseQuotes = True
            Escape = True
        End If

        If Escape Then
            Expression = Expression.Replace("'", "''")
        End If

        ret = If(UseQuotes, "'", "") & Expression & If(UseQuotes, "'", "")

        Return ret
    End Function

    Private Function PrepRightExpr(Expression As String, options As QueryOptions) As String
        Dim ret As String = ""

        Dim UseQuotes As Boolean = True
        Dim Escape As Boolean = False
        Select Case RightExpressionType
            Case QBuilder.ExpressionType.Auto
                If DatabaseTypeHelper.LooksLikeParam(Expression, options) OrElse IsNumeric(Expression) Then
                    UseQuotes = False
                End If
            Case QBuilder.ExpressionType.Literal
                If IsNumeric(Expression) Then
                    UseQuotes = False
                    Escape = True
                End If
            Case QBuilder.ExpressionType.Logical
                UseQuotes = False
            Case QBuilder.ExpressionType.Exact
                Return Expression
        End Select

        If Escape Then
            Expression = Expression.Replace("'", "''")
        End If

        ret = If(UseQuotes, "'", "") & Expression & If(UseQuotes, "'", "")

        Return ret
    End Function

    Public ReadOnly Property ShouldRender() As Boolean Implements ICriteriaComponent.HasContent
        Get
            Return True
        End Get
    End Property
End Class
